﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
using EstimationStudio.Core;

namespace EstimationStudio.UI
{
    public partial class Window : Form
    {
        public Window()
        {
            InitializeComponent();
        }

        private Boolean ChangesSaved { get; set; }

        private String SaveToPath { get; set; }

        private IEnumerable<ProjectPhase> ProjectPhases = new List<ProjectPhase>();

        private void Window_Load(object sender, EventArgs e)
        {
            ProjectPhases = new List<ProjectPhase>
            {
                new ProjectPhase("Initial Concept", new ErrorVariability(0.25, 4.0)),
                new ProjectPhase("Approved Product Definition", new ErrorVariability(0.5, 2.0)),
                new ProjectPhase("Requirements Complete", new ErrorVariability(0.67, 1.5)),
                new ProjectPhase("User Interface Design Complete", new ErrorVariability(0.8, 1.25)),
                new ProjectPhase("Detailed Design Complete", new ErrorVariability(0.9, 1.1)),
                new ProjectPhase("Hindsight", new ErrorVariability(1, 1)),
            };
            SetDirtyState(false);
            NewEstimate();
        }

        private void Window_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = !PromptToSaveEstimate();
        }

        private void menuItemNew_Click(object sender, EventArgs e)
        {
            NewEstimate();
        }

        private void menuItemOpen_Click(object sender, EventArgs e)
        {
            OpenEstimate();
        }

        private void menuItemSave_Click(object sender, EventArgs e)
        {
            SaveEstimate(UnloadEstimate());
        }
        
        private void menuItemSaveAs_Click(object sender, EventArgs e)
        {
            SaveEstimateAs(UnloadEstimate());
        }

        private void menuItemClose_Click(object sender, EventArgs e)
        {
            CloseEstimate();
        }

        private void menuItemExit_Click(object sender, EventArgs e)
        {
            CloseApplication();
        }

        private void Control_Changed(object sender, EventArgs e)
        {
            SetDirtyState(true);
        }

        private void NewEstimate()
        {
            if (!PromptToSaveEstimate())
                return;

            LoadEstimate(new Estimate
            {
                Title = "New Estimate",
                DateEstimated = DateTime.Now,
                ProjectPhase = ProjectPhases.First(),
                AdjustmentFactors = new List<AdjustmentFactor>
                {
                    new AdjustmentFactor("Applications (Business Area) Experience", "Teams that aren't familiar with the project's business area need significantly more time. This shouldn't be a surprise.", new AdjustmentVariability(1.22, 1.10, .88, .81, .81)),
                    new AdjustmentFactor("Database Size", "Large, complex databases require more effort project-wide. Total influence is moderate.", new AdjustmentVariability(0.9, 0.9, 1.14, 1.28, 1.28)),
                    new AdjustmentFactor("Developed for Reuse", "Software that is developed with the goal of later reuse can increase costs as much as 31%. This doesn't say whether the initiative actually succeeds. Industry experience has been that forward-looking reuse programs often fail.", new AdjustmentVariability(0.95, 0.95, 1.07, 1.15, 1.24)),
                    new AdjustmentFactor("Extent of Documentation Required", "Too much documentation can negatively affect the whole project. Impact is moderately high.", new AdjustmentVariability(0.81, 0.91, 1.11, 1.23, 1.23)),
                    new AdjustmentFactor("Language and Tools Experience", "Teams that have experience with the programming language and/or tools set work moderately more productively than teams that are climbing a learning curve. This is not a surprise.", new AdjustmentVariability(1.20, 1.09, 0.91, 0.84, 0.84)),
                    new AdjustmentFactor("Multisite Development", "Projects conducted by a team spread across multiple sites around the globe will take 56% more effort than projects that are conducted by a team co-located at one facility. Projects that are conducted at multiple sites, including out-sourced or offshore projects, need to take this effect seriously.", new AdjustmentVariability(1.22, 1.09, 0.93, 0.86, 0.78)),
                    new AdjustmentFactor("Personal Continuity (Turn over)", "Project turnover is expensive - in the top one third of influential factors.", new AdjustmentVariability(1.29, 1.12, 0.90, 0.81, 0.81)),
                    new AdjustmentFactor("Platform Experience", "Experience with the underlying technology platform affects overall project performance moderately.", new AdjustmentVariability(1.19, 1.09, 0.91, 0.85, 0.85)),
                    new AdjustmentFactor("Platform Volatility", "If the platform is unstable, development can take moderately longer. Projects should weigh this factor in their decision about when to adopt a new technology. This is one reason that systems projects tend to take longer than applications projects.", new AdjustmentVariability(0.87, 0.87, 1.15, 1.30, 1.30)),
                    new AdjustmentFactor("Product Complexity", "Product Complexity is the single most significant adjustment factor. Product complexity is largely determined by the type of software you're building.", new AdjustmentVariability(0.73, 0.87, 1.17, 1.34, 1.74)),
                    new AdjustmentFactor("Programmer Capability", "The skill of the programmers has an impact of a factor of almost 2 on overall project results.", new AdjustmentVariability(1.34, 1.15, 0.88, 0.76, 0.76)),
                    new AdjustmentFactor("Required Software Reliability", "More reliable systems take longer. This is one reason that ", new AdjustmentVariability(0.82, 0.92, 1.10, 1.26, 1.26)),
                    new AdjustmentFactor("Requirements Analyst Capability", "Competency in this area has the potential to reduce a project's overall effort from nominal more than any other factor.", new AdjustmentVariability(1.42, 1.19, 0.85, 0.71, 0.71)),
                    new AdjustmentFactor("Storage Constraint", "Working on a platform on which you're butting up against storage limitations moderatly increases project effort.", new AdjustmentVariability(1.00, 1.00, 1.05, 1.17, 1.46)),
                    new AdjustmentFactor("Time Constraint", "Minimizing response time increases effort across the board. This is one reason that systems projects and real-time projects tend to consume more effort than other projects of similar sizes.", new AdjustmentVariability(1.00, 1.00, 1.11, 1.29, 1.63)),
                    new AdjustmentFactor("Use of Software Tools", "Advanced tool sets can reduce effort significantly.", new AdjustmentVariability(1.17, 1.09, 0.90, 0.78, 0.78)),
                }
            }, ProjectPhases);

            SetDirtyState(false);
        }

        private void OpenEstimate()
        {
            if (!PromptToSaveEstimate())
                return;

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = ".xml|*.xml";
            openFileDialog.CheckFileExists = true;
            openFileDialog.ShowDialog();
            if (openFileDialog.FileName.Length == 0)
                return;

            LoadEstimate(new EstimateReader().Open(openFileDialog.FileName), ProjectPhases);
            SaveToPath = openFileDialog.FileName;
            SetDirtyState(false);
        }

        private void LoadEstimate(Estimate estimate, IEnumerable<ProjectPhase> projectPhases)
        {
            EstimateContainer.Controls.Clear();

            var estimateControl = new EstimateControl(projectPhases)
            {
                Dock = DockStyle.Fill
            };
            estimateControl.Changed += Control_Changed;
            estimateControl.LoadEstimate(estimate);
            EstimateContainer.Controls.Add(estimateControl);
        }

        private Estimate UnloadEstimate()
        {
            if (EstimateContainer.Controls == null || EstimateContainer.Controls.Count == 0)
                return null;

            return (EstimateContainer.Controls[0] as EstimateControl).UnloadEstimate();
        }

        private Boolean PromptToSaveEstimate()
        {
            if (ChangesSaved)
                return true;

            switch (MessageBox.Show("Do you want to save changes you made before exiting?", "Estimation Studio", MessageBoxButtons.YesNoCancel))
            {
                case DialogResult.Yes:
                    return SaveEstimate(UnloadEstimate());

                case DialogResult.No:
                    break;

                case DialogResult.Cancel:
                    return false;
            }
            return true;
        }

        private Boolean SaveEstimateAs(Estimate estimate)
        {
            SaveToPath = String.Empty;
           return SaveEstimate(estimate);
        }

        private Boolean SaveEstimate(Estimate estimate)
        {
            if (estimate == null)
                return false;

            if (String.IsNullOrWhiteSpace(SaveToPath))
            {
                SaveFileDialog saveFileDialog = new SaveFileDialog
                {
                    CheckFileExists = false,
                    DefaultExt = ".xml",
                    Filter = ".xml|*.xml",
                    FileName = estimate.EstimateId + ".xml",
                };
                if (saveFileDialog.ShowDialog() != DialogResult.OK)
                    return false;

                SaveToPath = saveFileDialog.FileName;
            }

            XElement serializationData = new EstimateWriter().SaveAsXml(estimate);
            serializationData.Save(SaveToPath);

            SetDirtyState(false);

            return true;
        }

        private void SetDirtyState(Boolean dirty)
        {
            ChangesSaved = !dirty;

            if (dirty)
            {
                if (!this.Text.EndsWith("*"))
                    this.Text += "*";
            }
            else
            {
                if (this.Text.EndsWith("*"))
                    this.Text = this.Text.Substring(0, this.Text.Length - 1);
            }
        }

        private void CloseEstimate()
        {
            SetDirtyState(false);
            EstimateContainer.Controls.Clear();
        }

        private void CloseApplication()
        {
            this.Close();
        }
    }
}